# encoding=utf-8
"""
代理中间件
"""
import logging

from scrapy.exceptions import IgnoreRequest
from zc_core.middlewares.proxies.proxy_facade import ProxyFacade
from zc_core.util.http_util import retry_request
from twisted.internet import defer
from twisted.internet.error import TimeoutError, DNSLookupError, \
    ConnectionRefusedError, ConnectionDone, ConnectError, \
    ConnectionLost, TCPTimedOutError
from scrapy.core.downloader.handlers.http11 import TunnelError
from twisted.web.client import ResponseFailed

logger = logging.getLogger(__name__)


class ProxyMiddleware(object):
    EXCEPTIONS_TO_RETRY = (defer.TimeoutError, TimeoutError, DNSLookupError,
                           ConnectionRefusedError, ConnectionDone, ConnectError,
                           ConnectionLost, TCPTimedOutError, ResponseFailed,
                           IOError, TunnelError)

    def __init__(self, settings):
        # 重构失败重试请求状态码
        self.retry_codes = set(int(x) for x in settings.getlist('RETRY_HTTP_CODES'))
        self.retry_codes.update(settings.getlist('CUSTOM_RETRY_CODES', []))
        # 添加自定义默认（常见于万变代理）
        self.retry_codes.update([407])
        ignore_codes = settings.getlist('CUSTOM_IGNORE_CODES', [])
        for code in ignore_codes:
            self.retry_codes.remove(code)

        self.proxy_facade = ProxyFacade(settings)

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler.settings)

    def process_request(self, request, spider):
        proxy = self.proxy_facade.get_proxy()
        if proxy:
            request.meta['proxy'] = "http://{}".format(proxy)
        else:
            raise BaseException('no proxy got ...')

    def process_response(self, request, response, spider):
        proxy = request.meta.get('proxy', '')
        if proxy:
            if response.status in self.retry_codes:
                # 标记失败
                self.proxy_facade.mark_fail(proxy)
                logger.info('响应重试[proxy=%s]: %s' % (proxy, str(response.status)))
                return retry_request(request)
            else:
                # 标记成功
                self.proxy_facade.mark_success(proxy)

        return response

    def process_exception(self, request, exception, spider):
        if exception and not isinstance(exception, IgnoreRequest):
            proxy = request.meta.get('proxy', '')
            spider.logger.error('异常重试[proxy=%s]: %s' % (proxy, str(type(exception))))
            if isinstance(exception, self.EXCEPTIONS_TO_RETRY):
                # 标记失败
                self.proxy_facade.mark_fail(proxy)

            return retry_request(request)
